-- Shader
-- create by zouyb
-- 渲染相关

-- 正常像素着色代码
local normalFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "vec4 c = texture2D(CC_Texture0, v_texCoord); \n" ..
    "gl_FragColor = c; \n"..
    "}";

-- 透明效果
local transparentFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "vec4 c = texture2D(CC_Texture0, v_texCoord); \n" ..
    "gl_FragColor = texture2D(CC_Texture0, v_texCoord) * v_fragmentColor; \n" ..
    "gl_FragColor.a *= 0.85; \n" ..
    "}";

-- 灰白效果
local grayFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "vec4 c = texture2D(CC_Texture0, v_texCoord); \n" ..
    "gl_FragColor.xyz = vec3(0.299*c.r + 0.587*c.g +0.114*c.b); \n"..
    "gl_FragColor.w = c.w; \n"..
    "gl_FragColor = gl_FragColor * v_fragmentColor;\n"..
    "}";

-- 灰白透明效果
local grayTransparentFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "vec4 c = texture2D(CC_Texture0, v_texCoord); \n" ..
    "gl_FragColor.xyz = vec3(0.299*c.r + 0.587*c.g +0.114*c.b); \n"..
    "gl_FragColor.r *= 0.7; \n" ..
    "gl_FragColor.g *= 0.7; \n" ..
    "gl_FragColor.b *= 0.7; \n" ..
    "gl_FragColor.a = 0.7 * c.a; \n"..
    "}";

-- 高亮效果
local highlightFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "uniform float light_factor; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "gl_FragColor = texture2D(CC_Texture0, v_texCoord) * v_fragmentColor; \n" ..
    "gl_FragColor.r *= 0.8; \n" ..
    "gl_FragColor.r += light_factor * gl_FragColor.a; \n" ..
    "gl_FragColor.g *= 0.8; \n" ..
    "gl_FragColor.g += light_factor * gl_FragColor.a; \n" ..
    "gl_FragColor.b *= 0.8; \n" ..
    "gl_FragColor.b += light_factor * gl_FragColor.a; \n" ..
    "}";

-- 变红效果
local redFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "gl_FragColor = texture2D(CC_Texture0, v_texCoord) * v_fragmentColor; \n" ..
    "gl_FragColor.r *= 0.8; \n" ..
    "gl_FragColor.r += 0.6 * gl_FragColor.a; \n" ..
    "gl_FragColor.g *= 0.8; \n" ..
    "gl_FragColor.b *= 0.8; \n" ..
    "}";

-- 变紫效果
local purpleFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "gl_FragColor = texture2D(CC_Texture0, v_texCoord) * v_fragmentColor; \n" ..
    "gl_FragColor.r *= 0.8; \n" ..
    "gl_FragColor.r += 0.2 * gl_FragColor.a; \n" ..
    "gl_FragColor.g *= 0.6; \n" ..
    "gl_FragColor.b *= 0.8; \n" ..
    "gl_FragColor.b += 0.4 * gl_FragColor.a; \n" ..
    "}";

-- 变暗效果
local darkFragSource = "#ifdef GL_ES \n" ..
    "precision mediump float; \n" ..
    "#endif \n" ..
    "varying vec4 v_fragmentColor; \n" ..
    "varying vec2 v_texCoord; \n" ..
    "void main(void) \n" ..
    "{ \n" ..
    "gl_FragColor = texture2D(CC_Texture0, v_texCoord) * v_fragmentColor; \n" ..
    "gl_FragColor.r *= 0.6; \n" ..
    "gl_FragColor.g *= 0.6; \n" ..
    "gl_FragColor.b *= 0.6; \n" ..
    "}";

-- 模糊效果
local blurFragSource = "#ifdef GL_ES \n" ..
"precision mediump float; \n" ..
"#endif \n" ..
"varying vec4 v_fragmentColor; \n" ..
"varying vec2 v_texCoord; \n" ..
"uniform vec2 resolution; \n" ..
"uniform float blurRadius; \n" ..
"uniform float sampleNum; \n" ..
"vec4 blur(vec2); \n" ..
"void main(void) \n" ..
"{ \n" ..
"    vec4 col = blur(v_texCoord); //* v_fragmentColor.rgb; \n" ..
"    gl_FragColor = vec4(col) * v_fragmentColor; \n" ..
"} \n" ..
"vec4 blur(vec2 p) \n" ..
"{ \n" ..
"    if (blurRadius > 0.0 && sampleNum > 1.0) \n" ..
"    { \n" ..
"        vec4 col = vec4(0); \n" ..
"        vec2 unit = 1.0 / resolution.xy; \n" ..
"        float r = blurRadius; \n" ..
"        float sampleStep = r / sampleNum; \n" ..
"        float count = 0.0; \n" ..
"        for(float x = -r; x < r; x += sampleStep) \n" ..
"        { \n" ..
"            for(float y = -r; y < r; y += sampleStep) \n" ..
"            { \n" ..
"                float weight = (r - abs(x)) * (r - abs(y)); \n" ..
"                col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)) * weight; \n" ..
"                count += weight; \n" ..
"            } \n" ..
"        } \n" ..
"        return col / count; \n" ..
"    } \n" ..
"} \n"

-- 加载渲染程序
function loadProgram(fragSource)
    local vertDefaultSource = "\n"..
        "attribute vec4 a_position; \n" ..
        "attribute vec2 a_texCoord; \n" ..
        "attribute vec4 a_color; \n"..
        "#ifdef GL_ES  \n"..
        "varying lowp vec4 v_fragmentColor;\n"..
        "varying mediump vec2 v_texCoord;\n"..
        "#else                      \n" ..
        "varying vec4 v_fragmentColor; \n" ..
        "varying vec2 v_texCoord;  \n"..
        "#endif    \n"..
        "void main() \n"..
        "{\n" ..
        "gl_Position = CC_PMatrix * a_position; \n"..
        "v_fragmentColor = a_color;\n"..
        "v_texCoord = a_texCoord;\n"..
        "}";

    local pProgram = cc.GLProgram:createWithByteArrays(vertDefaultSource, fragSource);
    if pProgram == nil then
        return nil;
    end

    pProgram:retain();

    pProgram:bindAttribLocation(cc.ATTRIBUTE_NAME_POSITION,cc.VERTEX_ATTRIB_POSITION);
    pProgram:bindAttribLocation(cc.ATTRIBUTE_NAME_COLOR,cc.VERTEX_ATTRIB_COLOR);
    pProgram:bindAttribLocation(cc.ATTRIBUTE_NAME_TEX_COORD,cc.VERTEX_ATTRIB_FLAG_TEX_COORDS);
    pProgram:link();
    pProgram:updateUniforms();
    return pProgram;
end

local programConfig = {
    ["normal"] = loadProgram(normalFragSource),
    ["trans"] = loadProgram(transparentFragSource),
    ["gray"] = loadProgram(grayFragSource),
    ["highlight"] = loadProgram(highlightFragSource),
    ["dark"] = loadProgram(darkFragSource),
    ["grayTrans"] = loadProgram(grayTransparentFragSource),
    ["red"] = loadProgram(redFragSource),
    ["purple"] = loadProgram(purpleFragSource),
    ["blur"] = loadProgram(blurFragSource),
    };

-- 获取着色器程序
function getProgram(name)
    if programConfig[name] == nil then
        return programConfig.normal;
    end

    return programConfig[name];
end

-- 对节点的子节点做还原
function resetNormalMode(node)
    if not node then
        return;
    end

    -- 着色程序
    local programs = table.values(programConfig);

    -- 对所有子节点做一样的事情
    local children = node:getChildren();
    local len = table.getn(children)
    for i = 1, len do
        local child = children[i];
        local sprite = tolua.cast(child,"cc.Sprite");

        if table.indexOf(programs, sprite:getGLProgram()) <= 1 then
            -- normal程序也不需要还原/找到一个是normal的就直接返回
            return;
        end

        if sprite then
            setNormalMode(sprite);
        end

        resetNormalMode(child);
    end
end

------------------------------------------------
-- 使图片变为常规效果
-- @function setNormalMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
function setNormalMode(node)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    if sprite.oldProgram then
        program = sprite.oldProgram;
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    -- 还原着色
    sprite:setGLProgram(program);

    node.grayMode = false;
end

------------------------------------------------
-- 使图片变为透明效果
-- @function setTransMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置透明模式，false表示退出透明模式
function setTransMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("trans");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);
end


------------------------------------------------
-- 使图片变为灰白色
-- @function setGrayMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置灰白模式，false表示退出灰白模式
function setGrayMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil and node:getVirtualRenderer().getSprite ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    -- 防止重复设置
    if node.grayMode and flag then
        return;
    elseif not node.grayMode and not flag then
        return;
    end

    local program;
    if flag then
        if sprite.oldProgram == nil then
            sprite.oldProgram = sprite:getGLProgram();
        end

        program = getProgram("gray");
    else
        if sprite.oldProgram then
            program = sprite.oldProgram;
        else
            program = getProgram("normal");
        end
    end

    if program == nil then
        return;
    end

    if flag then
        node.grayMode = true;
    else
        node.grayMode = nil;
    end

    sprite:setGLProgram(program);
end

------------------------------------------------
-- 使图片变为灰白透明效果
-- @function setGrayMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置灰白模式，false表示退出灰白模式
function setGrayTransMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("grayTrans");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);
end

------------------------------------------------
-- 使图片高亮
-- @function setHighLightMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置高亮模式，false表示退出高亮模式
-- @ppram factor 亮度因子
function setHighLightMode(node, flag, factor)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("highlight");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);

    if flag then
        factor = factor or 0.6;
        sprite:getGLProgramState():setUniformFloat("light_factor", factor);
    end
end

------------------------------------------------
-- 使图片变暗
-- @function setDarkMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @param flag true表示设置变暗模式，false表示退出变暗模式
function setDarkMode(node, flag)
    if node.enableOutline ~= nil then
        return;
    end

    local sprite = node;
    if node.getVirtualRenderer ~= nil
        and node:getVirtualRenderer().getSprite ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("dark");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);
end

------------------------------------------------
-- 使图片变红
-- @function setRedMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置变红模式，false表示退出变红模式
function setRedMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("red");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);
end

------------------------------------------------
-- 使图片变紫
-- @function setPurpleMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置变红模式，false表示退出变红模式
function setPurpleMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("purple");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);
end

------------------------------------------------
-- 使图片模糊
-- @function setBlurMode
-- @param node 需要设置的按钮组件, 只支持ImageView组件
-- @praam flag true表示设置模糊模式，false表示退出模糊模式
function setBlurMode(node, flag)
    local sprite = node;
    if node.getVirtualRenderer ~= nil then
        sprite = node:getVirtualRenderer():getSprite();
        if sprite == nil then
            return;
        end
    end

    local program;
    if flag then
        program = getProgram("blur");
    else
        program = getProgram("normal");
    end

    if program == nil then
        return;
    end

    sprite:setGLProgram(program);

    if flag then
        local size = sprite:getTexture():getContentSizeInPixels();
        sprite:getGLProgramState():setUniformVec2("resolution", cc.p(size.width, size.height));
        sprite:getGLProgramState():setUniformFloat("blurRadius", 5.0);
        -- 采用个数太大可能导致效率很低
        sprite:getGLProgramState():setUniformFloat("sampleNum", 7.0);
    end
end
